import { isRecord } from './object';
import { isString } from './string';

/**
 * 提取任意类型为 Error Message，如果未包含有效的 Message，返回空字符串。
 *
 * 特别针对 Typescript 4 改版后的使用：
 *
 * ```typescript
 * try {
 *   throw 'hello world';
 * } catch (e) {
 *   // 这里的 e 在 Typescript 4 以后，被强制限定为 unknown 类型，不能断定为 Error 实例
 *   console.log(extractErrorMessage(e) || '未知错误');
 * }
 * ```
 * @param err
 */
export const extractErrorMessage = (err: Error | string | unknown | undefined): string => {
  if (err == null) return '';
  if (err instanceof Error) {
    return err.message;
  } else if (isString(err)) {
    return err;
  } else if (isRecord<{ msg?: string; message?: string }>(err)) {
    if (isString(err?.message)) return err.message;
    if (isString(err?.msg)) return err.msg;
  }
  return '';
};

// @ts-ignore args P
export type ShouldCallback<P = any> = (v: unknown, ...args: P) => boolean; // eslint-disable-line @typescript-eslint/no-explicit-any
export type ShouldCallbackInferLastParams<C> = C extends ShouldCallback<infer P> ? P : unknown;

export const should =
  <C extends ShouldCallback, P extends ShouldCallbackInferLastParams<ShouldCallback>>(
    callback: C,
    ...args: P[]
  ): ((v: unknown) => boolean) =>
  (v: unknown): boolean => {
    try {
      return callback(v, ...args);
    } catch (e) {
      return false;
    }
  };

export const shouldNot =
  <C extends ShouldCallback, P extends ShouldCallbackInferLastParams<ShouldCallback>>(callback: C, ...args: P[]) =>
  (v: unknown): boolean => {
    try {
      return !callback(v, ...args);
    } catch (e) {
      return true;
    }
  };
